/*
  Copyright (c) 2002, 2003 Russell Morris
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, 
  are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, 
      this list of conditions and the following disclaimer. 
      
    * Redistributions in binary form must reproduce the above copyright notice, 
      this list of conditions and the following disclaimer in the documentation 
      and/or other materials provided with the distribution. 
      
    * The names of the contributors may not be used to endorse or promote 
      products derived from this software without specific prior written permission. 
      
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
    THE POSSIBILITY OF SUCH DAMAGE.
*/

namespace WRM.Windows.Forms.Design
{
    using System;
    using System.Reflection;
    using System.Drawing;
    using System.Drawing.Design;
    using System.Windows.Forms;
    using System.Windows.Forms.Design;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Collections;
    using System.Runtime.InteropServices;

    /// <summary>
    ///    The custom designer class for <see cref="PropertyTree"/>.
    /// </summary>
    /// <remarks>
    /// <para>
    /// Adds designer
    /// verbs to the context menu for <see cref="PropertyTree"/>, and generally makes 
    /// things operate in the design environment like they would in the runtime environment.
    /// </para>
    /// </remarks>
    public class PropertyTreeDesigner : ControlDesigner
    {

    #region Win32 constants and imports
        /// <summary>
        ///   WM_USER message constant
        /// </summary>
        public const int WM_USER = 0x0400;

        /// <summary>
        ///   WM_REFLECT message constant
        /// </summary>
        public const int WM_REFLECT = WM_USER + 0x1c00;

        /// <summary>
        /// WM_MOUSEMOVE
        /// </summary>
        private const int WM_MOUSEMOVE      = 0x0200;
    
        /// <summary>
        /// WM_LBUTTONDOWN    
        /// </summary>
        private const int WM_LBUTTONDOWN    = 0x0201;
	  
        /// <summary>
        /// WM_LBUTTONUP      
        /// </summary>
        private const int WM_LBUTTONUP      = 0x0202;
    
        /// <summary>
        /// WM_LBUTTONDBLCLK  
        /// </summary>
        private const int WM_LBUTTONDBLCLK  = 0x0203;
    
        /// <summary>
        /// WM_RBUTTONDOWN    
        /// </summary>
        private const int WM_RBUTTONDOWN    = 0x0204;
    
        /// <summary>
        /// WM_RBUTTONUP      
        /// </summary>
        private const int WM_RBUTTONUP      = 0x0205;
    
        /// <summary>
        /// WM_RBUTTONDBLCLK  
        /// </summary>
        private const int WM_RBUTTONDBLCLK  = 0x0206;
    
        /// <summary>
        /// MK_CONTROL        
        /// </summary>
        private const int MK_CONTROL        = 0x0008;

        /// <summary>
        /// TV_FIRST              
        /// </summary>
        private const int TV_FIRST              = 0x1100;
    
        /// <summary>
        /// TVM_HITTEST           
        /// </summary>
        private const int TVM_HITTEST           = TV_FIRST + 17;
    
        /// <summary>
        /// TVHT_NOWHERE          
        /// </summary>
        private const int TVHT_NOWHERE          = 0x0001;
    
        /// <summary>
        /// TVHT_ONITEMICON       
        /// </summary>
        private const int TVHT_ONITEMICON       = 0x0002;
    
        /// <summary>
        /// TVHT_ONITEMLABEL      
        /// </summary>
        private const int TVHT_ONITEMLABEL      = 0x0004;
    
        /// <summary>
        /// TVHT_ONITEMSTATEICON  
        /// </summary>
        private const int TVHT_ONITEMSTATEICON  = 0x0040;
    
        /// <summary>
        /// TVHT_ONITEM           
        /// </summary>
        private const int TVHT_ONITEM           = TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMSTATEICON;
    
        /// <summary>
        /// TVHT_ONITEMINDENT     
        /// </summary>
        private const int TVHT_ONITEMINDENT     = 0x0008;
    
        /// <summary>
        /// TVHT_ONITEMBUTTON     
        /// </summary>
        private const int TVHT_ONITEMBUTTON     = 0x0010;
    
        /// <summary>
        /// TVHT_ONITEMRIGHT      
        /// </summary>
        private const int TVHT_ONITEMRIGHT      = 0x0020;
    
        /// <summary>
        /// TVHT_ABOVE            
        /// </summary>
        private const int TVHT_ABOVE            = 0x0100;
    
        /// <summary>
        /// TVHT_BELOW            
        /// </summary>
        private const int TVHT_BELOW            = 0x0200;
    
        /// <summary>
        /// TVHT_TORIGHT          
        /// </summary>
        private const int TVHT_TORIGHT          = 0x0400;
    
        /// <summary>
        /// TVHT_TOLEFT           
        /// </summary>
        private const int TVHT_TOLEFT           = 0x0800;

        /// <summary>
        /// The Win32 POINT struct
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
            private struct POINT
        {
            /// <summary>
            /// The x coordinate of the point
            /// </summary>
            public int x;
            /// <summary>
            /// The y coordinate of the point
            /// </summary>
            public int y;
        }

        /// <summary>
        /// The Win32 TVHITTESTINFO struct
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
            private struct TVHITTESTINFO
        {
            /// <summary>
            /// The point at which to hit test
            /// </summary>
            public POINT pt;
            /// <summary>
            /// Result of the hit test
            /// </summary>
            public int flags;
            /// <summary>
            /// An HTREEITEM handle
            /// </summary>
            public IntPtr hItem;
        }
      
        /// <summary>
        /// Enumeration identifying parts of the node that can be
        /// detected during hit-testing.
        /// </summary>
        [Flags]
            private enum NodePart
        {
            /// <summary>
            /// Above the node
            /// </summary>
            Above = TVHT_ABOVE,
            /// <summary>
            /// Below the node
            /// </summary>
            Below = TVHT_BELOW,
            /// <summary>
            /// To the left of the node
            /// </summary>
            ToLeft = TVHT_TOLEFT,
            /// <summary>
            /// To the right of the node
            /// </summary>
            ToRight = TVHT_TORIGHT,
            /// <summary>
            /// Not near a node
            /// </summary>
            Nowhere = TVHT_NOWHERE,
    
            /// <summary>
            /// On the item itself
            /// </summary>
            OnItem = TVHT_ONITEM,
            /// <summary>
            /// On the item's plus/minus button
            /// </summary>
            OnItemButton = TVHT_ONITEMBUTTON,
            /// <summary>
            /// On the item's icon
            /// </summary>
            OnItemIcon = TVHT_ONITEMICON,
            /// <summary>
            /// On the space to the left of the item
            /// </summary>
            OnItemIndent = TVHT_ONITEMINDENT,
            /// <summary>
            /// On the text label of the item
            /// </summary>
            OnItemLabel = TVHT_ONITEMLABEL,
            /// <summary>
            /// On the item's state icon
            /// </summary>
            OnItemStateIcon = TVHT_ONITEMSTATEICON,
            /// <summary>
            /// To the right of the item's label
            /// </summary>
            OnItemRight = TVHT_ONITEMRIGHT
        }

        /// <summary>
        /// Import the Win32 API <c>SendMessage</c> function.
        /// </summary>
        [DllImport("user32.dll", CharSet=CharSet.Auto)]
        private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, ref TVHITTESTINFO hti);

    #endregion

    #region Member variables
        /// <summary>
        ///   A collection of our designer verbs
        /// </summary>
        private DesignerVerbCollection mVerbs;
    
        /// <summary>
        ///   A reference to the instance of PropertyTree that we're 
        /// designing
        /// </summary>
        private PropertyTree mPropertyTree;
    
        /// <summary>
        ///   A reference to the IDesignerHost the VS.NET environment
        /// provides us with.
        /// </summary>
        private IDesignerHost mDesignerHost;
    
        /// <summary>
        ///   A reference to the selection service that is provided by
        /// the VS.NET IDE.
        /// </summary>
        private ISelectionService mSelectionService;

        /// <summary>
        ///   A reference to the toolbox service that is provided by the
        /// VS.NET IDE.
        /// </summary>
        private IToolboxService mToolboxService;

        /// <summary>
        /// A reference tot he menu service that is provided by
        /// the VS.NET IDE.
        /// </summary>
        private IMenuCommandService mMenuService;

        /// <summary>
        ///   The pane that is the drop-target of a drag-n-drop operation
        /// </summary>
        private PaneNode mTargetPaneNode;

        // Variables used for drag-n-drop operations
        /// <summary>
        /// Whether or not the mouse button is currently pressed
        /// </summary>
        private bool mMouseDown;

        /// <summary>
        /// Whether or not an item is being dragged
        /// </summary>
        private bool mIsDragging;

        /// <summary>
        /// The <c>PaneNode</c> that is being dragged
        /// </summary>
        private PaneNode mDragPaneNode;

        /// <summary>
        /// The current target of the drag-n-drop operation
        /// </summary>
        private PaneNode mDropTargetNode;

        /// <summary>
        /// The position that the cursor is currently located
        /// </summary>
        private Point mDragPt;
    #endregion

        /// <summary>
        /// Default constructor
        /// </summary>
        public PropertyTreeDesigner()
        {
            mVerbs = new DesignerVerbCollection();
            mVerbs.Add(new DesignerVerb("Add PropertyPane",
                new EventHandler(OnAddPane)));
            mVerbs.Add(new DesignerVerb("Add PropertyPane as child",
                new EventHandler(OnAddPaneAsChild)));
            mVerbs.Add(new DesignerVerb("Remove PropertyPane",
                new EventHandler(OnRemovePane)));
            mVerbs[2].Enabled = false;
      
            mIsDragging = false;
            mMouseDown = false;
            mDragPaneNode = null;
            mDropTargetNode = null;
            mDragPt.X = -10000;
            mDragPt.Y = -10000;
        }
  
        /// <summary>
        /// Gets the list of <see cref="PropertyPane"/> objects that exist in this 
        /// <c>PropertyTree</c>
        /// </summary>
        /// <value>
        /// Builds and returns an <see cref="ICollection"/> of all <see cref="PropertyPane"/>s
        /// contained in this <c>PropertyTree</c>.
        /// </value>
        /// <remarks>
        /// This collection is used to facilitate clipboard copy &amp; paste operations.
        /// This ensures that when you copy a <c>PropertyTree</c>, it will carry
        /// along with it all the <see cref="PropertyPane"/>s that have been added to it.
        /// </remarks>
        /// <returns>
        /// Return the list of <see cref="PropertyPane"/> objects that exist in this 
        /// <c>PropertyTree</c>
        /// </returns>
        public override ICollection AssociatedComponents
        {
            get
            {
                ArrayList comps = new ArrayList();

                // Add each of our property panes to the list of associated components
                // 
                foreach(PaneNode pi in GetAllChildPaneNodes(mPropertyTree.mRootPaneNode))
                    comps.Add(pi.PropertyPane);

                return comps;
            }
        }

        /// <summary>
        /// Gets a collection of custom <see cref="DesignerVerb"/>s that will appear
        /// on VS.NET's context menu when a <c>PropertyTree</c> is selected
        /// at design-time.
        /// </summary>
        /// <value>
        /// The <see cref="DesignerVerbCollection">DesignerVerbCollection</see>
        /// that was created in our constructor.
        /// </value>
        public override DesignerVerbCollection Verbs
        {
            get
            {
                return mVerbs;
            }
        }
    
        /// <summary>
        ///   Setup our internal references to the <see cref="PropertyTree">
        ///   PropertyTree</see>, and various <see cref="IDesignerHost">
        ///   IDesignerHost</see> services
        /// </summary>
        /// <param name="component">The component that is being attached to this designer</param>
        public override void Initialize(IComponent component)
        {
            base.Initialize(component);

            if(Control is PropertyTree)
            {
                mPropertyTree = (PropertyTree)Control;
    
                // Hook PaneActivated
                // 
                mPropertyTree.PaneActivated += 
                    new PropertyTree.PaneActivatedEventHandler(OnPaneActivated); 

                mDesignerHost = (IDesignerHost)
                    GetService(typeof(IDesignerHost));

                mSelectionService = (ISelectionService)
                    GetService(typeof(ISelectionService));

                mToolboxService = (IToolboxService)
                    GetService(typeof(IToolboxService));

                mMenuService = (IMenuCommandService)
                    GetService(typeof(IMenuCommandService));

                mPropertyTree.AllowDrop = true;
                this.EnableDragDrop(true);
            }
        }
    
        /// <summary>
        /// Gets the <see cref="SelectionRules">SelectionRules</see> for this 
        /// component
        /// </summary>
        /// <value>
        /// Any sort of selection for is allowed for 
        /// <see cref="PropertyTree">PropertyTree</see>, so
        /// the value returned is a logical OR of the values
        /// in <see cref="SelectionRules"/>.
        /// </value>
        public override SelectionRules SelectionRules
        {
            get 
            {
                return SelectionRules.AllSizeable | 
                    SelectionRules.Moveable |
                    SelectionRules.Visible;
            }
        }
    
        /// <summary>
        ///   Handles the "Add PropertyPane..." designer verb.
        /// </summary>
        /// <param name="sender">
        /// The sender of this event
        /// </param>
        /// <param name="e">
        /// Information about the event
        /// </param>
        /// <remarks>
        ///   <para>
        ///   A new <see cref="PropertyPane"/> is creating and added to the
        ///   designed <see cref="PropertyTree"/> as a sibling of the
        ///   node identified by <see cref="PropertyTree.SelectedPaneNode"/>.
        ///   </para>
        ///   <para>
        ///   After being added, the new <see cref="PropertyPane"/> is selected
        ///   as the active designer component.
        ///   </para>
        /// </remarks>
        public void OnAddPane(object sender, EventArgs e)
        {
            // Determine what the current pane is, and add a new PropertyPane
            // as its sibling.  Note the use of CreateComponent() to tell the
            // VS.NET design environment that we are creating a new instance of
            // a control that should be persisted in code.
            // 
            string name = GenerateNewPaneName();
            PaneNode curPaneNode = mPropertyTree.SelectedPaneNode;
            PaneNode parentNode = null;
      
            if(curPaneNode != null)
                parentNode = curPaneNode.Parent;

            PropertyPane pp = 
                mDesignerHost.CreateComponent(typeof(PropertyPane),name) as PropertyPane;

            //Add the pane to the PropertyTree
            pp.Text= name;
            PaneNode paneNode = null;
      
            if(parentNode == null)
                paneNode = mPropertyTree.PaneNodes.Add(pp);
            else
                paneNode = parentNode.PaneNodes.Add(pp);
      
            //Programatically select the pane we just added
            mPropertyTree.SelectedPaneNode = paneNode;
        }
    
        /// <summary>
        ///   Handle the "Add PropertyPane as child..." designer verb
        /// </summary>
        /// <param name="sender">
        /// The sender of this event
        /// </param>
        /// <param name="e">
        /// Information about the event
        /// </param>
        /// <remarks>
        ///   <para>
        ///   A new <see cref="PropertyPane"/> is creating and added to the
        ///   designed <see cref="PropertyTree"/> as a child of the
        ///   node identified by <see cref="PropertyTree.SelectedPaneNode"/>.
        ///   </para>
        ///   <para>
        ///   After being added, the new <see cref="PropertyPane"/> is selected
        ///   as the active designer component.
        ///   </para>
        /// </remarks>
        public void OnAddPaneAsChild(object sender, EventArgs e)
        {
            // Determine what the current pane is, and add a new PropertyPane
            // as its sibling.  Note the use of CreateComponent() to tell the
            // VS.NET design environment that we are creating a new instance of
            // a control that should be persisted in code.
            // 
            string name = GenerateNewPaneName();
            PaneNode curPaneNode = mPropertyTree.SelectedPaneNode;
            PropertyPane pp = 
                mDesignerHost.CreateComponent(typeof(PropertyPane),name) as PropertyPane;

            //Add the pane to the PropertyTree
            pp.Text= name;
            PaneNode paneNode = null;
      
            if(curPaneNode == null)
                paneNode = mPropertyTree.PaneNodes.Add(pp);
            else
                paneNode = curPaneNode.PaneNodes.Add(pp);
      
            //Programatically select the pane we just added
            mPropertyTree.SelectedPaneNode = paneNode;
        }

        /// <summary>
        ///   Handle the "Remove PropertyPane..." designer verb.
        /// </summary>
        /// <param name="sender">
        /// The sender of this event
        /// </param>
        /// <param name="e">
        /// Information about the event
        /// </param>
        /// <remarks>
        ///   <para>
        ///   The node identified by <see cref="PropertyTree.SelectedPaneNode"/>
        ///   is removed from the <c>PropertyTree</c>.
        ///   </para>
        ///   <para>
        ///   Afterwards, there is no longer a selected designer component.
        ///   </para>
        /// </remarks>
        public void OnRemovePane(object sender, EventArgs e)
        {
            TreeNode n = mPropertyTree.TreeView.SelectedNode;
            PaneNode selPaneNode = mPropertyTree.SelectedPaneNode;
      
            PaneNode[] deletedPanes = GetAllChildPaneNodes(selPaneNode);

            // Delete all child PropertyPanes of this PropertyPane first.
            // We must actually tell the design environment to remove these
            // instances by calling DestroyComponent - this makes sure that
            // they are no longer persisted in code.
            foreach(PaneNode deletedPane in deletedPanes)
            {
                // Each call to DestroyCompent will end up removing 'deletedPane'
                // from the Controls collection of the PropertyTree
                mDesignerHost.DestroyComponent(deletedPane.PropertyPane);
            }

            // Once its children are removed, delete the component itself.
            mDesignerHost.DestroyComponent(selPaneNode.PropertyPane);

            selPaneNode.Parent.PaneNodes.Remove(selPaneNode);
        }
    
        /// <summary>
        ///   Updates the available menu commands when a new 
        ///   <see cref="PropertyPane"/> is selected.
        /// </summary>
        /// <param name="sender">
        /// The sender of this event
        /// </param>
        /// <param name="psea">
        /// Information about the event
        /// </param>
        public void OnPaneActivated(PropertyTree sender,
            PaneSelectionEventArgs psea)
        {
            UpdateMenuCommands();
        }
    
        /// <summary>
        ///   Checks to see if the dragged node is a parent of the drop-target.
        /// </summary>
        /// <param name="drag">
        /// The <see cref="TreeNode"/> being dragged
        /// </param>
        /// <param name="target">
        /// The <see cref="TreeNode"/> that is the current target of the drop operation
        /// </param>
        /// <returns><c>true</c> if drag node is a parent of the drop target node</returns>
        protected bool IsParent(TreeNode drag, TreeNode target)
        {
            if(target.Parent != null)
            {
                if(target.Parent == drag)
                    return true;
                else
                    return IsParent(drag,target.Parent);
            }
            else
                return false;
        }
    
        /// <summary>
        ///   Perform our design-time drag n'drop of pane nodes
        /// </summary>
        /// <param name="dragPaneNode">
        /// The <c>PaneNode</c> that is being dragged
        /// </param>
        /// <param name="dropTargetNode">
        /// The <c>PaneNode</c> that <paramref name="dragPaneNode"/> is being dropped onto
        /// </param>
        /// <param name="dropAsChild">
        /// Boolean value indicating whether or not <paramref name="dragPaneNode"/> is to become 
        /// a child of <paramref name="dropTargetNode"/>.
        /// </param>
        /// <remarks>
        /// This method filters out and deals with any possible screwy values that could be 
        /// passed into it.  If either <paramref name="dragPaneNode"/> or 
        /// <paramref name="dropTargetNode"/> is <c>null</c>, or if 
        /// <paramref name="dragPaneNode"/> is a parent of 
        /// <paramref name="dropTargetNode"/>, this method gracefully no-ops.
        /// </remarks>
        protected void DoDragDrop(PaneNode dragPaneNode, PaneNode dropTargetNode, bool dropAsChild)
        {
            int    newDropIndex;
            PaneNodeCollection dropNodesCol;
      
            // Bail out if either is null
            if(dragPaneNode == null || dropTargetNode == null)
                return;

            // Bail out if they are the same, or if dragPane is a parent of dropTarget
            if(dragPaneNode == dropTargetNode)
                return;

            if(IsParent(dragPaneNode.TreeNode,dropTargetNode.TreeNode))
            {
                MessageBox.Show("Cannot drop " + dragPaneNode.Text + " onto " + dropTargetNode.Text + ".\n\n" +
                    dragPaneNode.Text + " is an ancestor of " + dropTargetNode.Text + ".","Drag-drop error",
                    MessageBoxButtons.OK,MessageBoxIcon.Stop);
                return;
            }
     
            // Find the appropriate node collection
            if(dropAsChild)
            {
                dropNodesCol = dropTargetNode.PaneNodes;
                newDropIndex = 0;
            }
            else
            {
                dropNodesCol = dropTargetNode.Parent.PaneNodes;
                newDropIndex = dropTargetNode.Index;
            }
      
            dragPaneNode.Parent.PaneNodes.Remove(dragPaneNode);
            dragPaneNode.IndexInternal = newDropIndex;

            dropNodesCol.Add(dragPaneNode);

            mPropertyTree.SelectedPaneNode = dragPaneNode;

            RaiseComponentChanging(
                TypeDescriptor.GetProperties(typeof(PropertyTree))["PaneNodes"]);
            RaiseComponentChanged(
                TypeDescriptor.GetProperties(typeof(PropertyTree))["PaneNodes"],
                null,
                null);
        }

        /// <summary>
        ///   Figure out whether or not the given point is on a node's plus/minus button.
        /// </summary>
        /// <param name="pt">
        /// The point that was clicked
        /// </param>
        /// <returns><c>true</c> if the point is on a node's plus/minus button</returns>
        private bool IsOnPlusMinus(Point pt)
        {
            TVHITTESTINFO htinfo = new TVHITTESTINFO();

            htinfo.pt.x = pt.X;
            htinfo.pt.y = pt.Y;
      
            int i = SendMessage(
                mPropertyTree.TreeView.Handle,
                TVM_HITTEST,
                0,
                ref htinfo);

            return ((htinfo.flags & (int)NodePart.OnItemButton) != 0);
        }


        /// <summary>
        /// Ensures that the designed <see cref="PropertyTree"/> is the currently
        /// selected design-time component.
        /// </summary>
        private void EnsureSelected()
        {
            if(!mSelectionService.GetComponentSelected(mPropertyTree))
            {
                ArrayList sel = new ArrayList();
                sel.Add(mPropertyTree);
                mSelectionService.SetSelectedComponents(sel,SelectionTypes.Replace);
            }
        }

        /// <summary>
        /// Low-level access to the designed component's window procedure.
        /// </summary>
        /// <param name="m">
        /// The windows message that was passed to our designed component's
        /// window procedure.
        /// </param>
        /// <remarks>
        /// <para>
        /// This function is used to handle all mouse-related input that
        /// occurs over the <see cref="PropertyTree"/>'s internal
        /// <see cref="TreeView"/>.  We want the design-time behavior to 
        /// mimick the run-time behavior as closely as possible, but we also 
        /// want special functionality (like drag-and-drop rearrangement) 
        /// during design-time.
        /// </para>
        /// <para>
        /// In order to get this behavior, manual intervention is required 
        /// when it comes to processing mouse messages for the 
        /// <see cref="PropertyTree"/>'s <see cref="TreeView"/>.  This code
        /// must intercept all mouse-related messages that are destined for
        /// this <c>TreeView</c> and force the expected behavior.
        /// </para>
        /// </remarks>
        protected override void WndProc(ref Message m) 
        {
            Point p;
            TreeNode n;

            if(mPropertyTree.TreeView.Created  && 
                (m.HWnd == mPropertyTree.TreeView.Handle ||
                m.HWnd == mPropertyTree.Handle) )
            {
        
                switch(m.Msg)
                {
                        // If the user has pressed the left mouse button, select a node
                        // in the TreeView if necessary
                        // 
                    case WM_LBUTTONDOWN:
                        p = new Point(m.LParam.ToInt32());
                        n = mPropertyTree.TreeView.GetNodeAt(p);
                        EnsureSelected();
                        if(n != null)
                        {
                            mPropertyTree.SelectPaneByNode(n);
                            if(IsOnPlusMinus(p))
                                n.Toggle();

                            // Setup dragging vars
                            // 
                            mMouseDown = true;
                            mIsDragging = false;
                            mDragPaneNode = mPropertyTree.SelectedPaneNode;
                            mDragPt = new Point(m.LParam.ToInt32());
                        }
                        //base.WndProc(ref m);
                        BaseWndProc(ref m);
                        break;
          
                    case WM_MOUSEMOVE:
                        if(mMouseDown && mDragPaneNode != null)
                        {
                            if(!mDragPt.Equals(new Point(m.LParam.ToInt32())))
                            {
                                mIsDragging = true;

                                PaneNode newTargetNode = mPropertyTree.GetPaneNodeAt(new Point(m.LParam.ToInt32()));
                                if(newTargetNode != mDropTargetNode)
                                {
                                    if(mDropTargetNode != null)
                                        DropHilite(mDropTargetNode,false);
                                    if(newTargetNode != null)
                                        DropHilite(newTargetNode,true);

                                    mDropTargetNode = newTargetNode;
                                }
                            }
                        }
                        base.WndProc(ref m);
                        break;

                    case WM_LBUTTONUP:
                        if(mIsDragging)
                        {
                            bool dropAsChild = ((m.WParam.ToInt32() & MK_CONTROL) != 0);
                            DoDragDrop(mDragPaneNode,mDropTargetNode,dropAsChild);
              
                            if(mDropTargetNode != null)
                                DropHilite(mDropTargetNode,false);

                        }

                        mMouseDown = false;
                        mIsDragging = false;
                        mDragPaneNode = null;
                        mDropTargetNode = null;

                        base.WndProc(ref m);
                        break;

                    case WM_RBUTTONDOWN:
                        BaseWndProc(ref m);
                        break;
          
                    case WM_RBUTTONUP:
                        p = new Point(m.LParam.ToInt32());
                        Point menuPos = mPropertyTree.PointToScreen(p);
                        n = mPropertyTree.TreeView.GetNodeAt(p);
                        mPropertyTree.SelectPaneByNode(n);
                        EnsureSelected();
                        mMenuService.ShowContextMenu(MenuCommands.SelectionMenu,menuPos.X,menuPos.Y);
                        BaseWndProc(ref m);
                        break;
          
                    default:
                        base.WndProc(ref m);
                        break;
                }
            }
            else
            {
                base.WndProc(ref m);
            }
        }

        /// <summary>
        /// Use the VS.NET's <see cref="ISelectionService"/> to programmatically select
        /// a <see cref="PropertyPane"/> in the Forms designer.
        /// </summary>
        /// <param name="pane">The <c>PaneNode</c> to select</param>
        /// <remarks>
        /// This is done when a new <see cref="PropertyPane">PropertyPane</see>
        /// is selected so that its properties will appear in the VS.NET IDE's 
        /// property browser.
        /// </remarks>
        protected void SelectPane(PaneNode pane)
        {
            object[] o = new object[1];
            o[0] = pane.PropertyPane;
            mSelectionService.SetSelectedComponents(o,SelectionTypes.Replace);
        }

        /// <summary>
        ///   Update the available menu commands, based on the currently selected
        /// <see cref="PropertyPane">PropertyPane</see>.
        /// </summary>
        protected void UpdateMenuCommands()
        {
            PaneNode paneNode = mPropertyTree.SelectedPaneNode;

            if(paneNode == null)
            {
                // If no pane is selected, you can add panes to the root
                Verbs[0].Enabled = true;
        
                // If no pane is selected, you cannot add a pane as a child
                Verbs[1].Enabled = false;

                // If no pane is selected, you cannot remove the pane
                Verbs[2].Enabled = false;
            }
            else
            {
                // Add a sibling pane
                Verbs[0].Enabled = true;

                // Add a child pane
                Verbs[1].Enabled = true;

                // Remove selected pane
                Verbs[2].Enabled = true;
            }
        }

        /// <summary>
        /// Perform hit-testing on the given <paramref name="point"/> to determine
        /// if it is on a node or not.
        /// </summary>
        /// <param name="point">
        /// The <see cref="System.Drawing.Point">Point</see>, in screen coordinates,
        /// to test.
        /// </param>
        /// <returns>
        /// Returns <c>true</c> if the cursor is over the <c>TreeView</c>, to indicate that
        /// the user can click on the nodes in the <c>TreeView</c>.  Returns <c>false</c> otherwise
        /// to let the user move the control around on the form.
        /// </returns>
        protected override bool GetHitTest(System.Drawing.Point point)
        {
            Point transPoint = mPropertyTree.TreeView.PointToClient(point);

            return mPropertyTree.TreeView.Bounds.Contains(transPoint);
        }

        /// <summary>
        /// Handle drag-drop from the VS.NET ToolBox
        /// </summary>
        /// <param name="de">
        /// Object describing the drag-drop event that has occured
        /// </param>
        /// <remarks>
        /// <para>
        /// If the <c>Ctrl</c> key is down, then the dropped
        /// <see cref="PropertyPane"/> is added as a child of the
        /// drop-target.  Otherwise, it is added as a sibling of
        /// the drop-target.
        /// </para>
        /// <para>
        /// Dragged objects that did not originate from the VS.NET
        /// ToolBox, that do not derive from <see cref="PropertyPane"/>, 
        /// or that do derive from <see cref="SharedPropertyPane"/> 
        /// are ignored.
        /// </para>
        /// </remarks>
        protected override void OnDragDrop(System.Windows.Forms.DragEventArgs de)
        {
            base.OnDragDrop(de);
            if (mToolboxService.IsToolboxItem(de.Data, mDesignerHost))
            {
                PaneNodeCollection parentNodes = null;

                if(mTargetPaneNode == null)
                {
                    parentNodes = mPropertyTree.PaneNodes;
                }
                else
                {
                    bool bAddAsChild = (de.KeyState & 8) != 0;
          
                    if(bAddAsChild)
                        parentNodes = mTargetPaneNode.PaneNodes;
                    else
                        parentNodes = mTargetPaneNode.Parent.PaneNodes;
          
                    DropHilite(mTargetPaneNode,false);
                }

                ToolboxItem item = mToolboxService.DeserializeToolboxItem(de.Data, mDesignerHost);
                Type t = Type.GetType(item.TypeName);

                if(!typeof(PropertyPane).IsAssignableFrom(t))
                {
                    MessageBox.Show("You can only add classes that derive from PropertyPane to the PropertyTree.");
                    return;
                }

                if(typeof(SharedPropertyPane).IsAssignableFrom(t))
                {
                    MessageBox.Show("You cannot add classes that derive from SharedPropertyPane to the PropertyTree at design-time.");
                    return;
                }

                string name = GenerateNewPaneName();

                IComponent comp = mDesignerHost.CreateComponent(t,name);
                PropertyPane newPane = comp as PropertyPane;
                newPane.Text = name;

                PaneNode newPaneNode = parentNodes.Add(newPane);
                mPropertyTree.SelectedPaneNode = newPaneNode;

                mToolboxService.SelectedToolboxItemUsed();

                RaiseComponentChanging(
                    TypeDescriptor.GetProperties(typeof(PropertyTree))["PaneNodes"]);
                RaiseComponentChanged(
                    TypeDescriptor.GetProperties(typeof(PropertyTree))["PaneNodes"],
                    null,
                    null);
            }
      
            mTargetPaneNode = null;
        }

        /// <summary>
        ///   Handle the drag-enter event.
        /// </summary>
        /// <param name="de">Object describing the drag-drop event that has occured</param>
        /// <remarks>
        /// <para>
        /// Dragged objects that did not originate from the VS.NET
        /// ToolBox, that do not derive from <see cref="PropertyPane"/>, 
        /// or that do derive from <see cref="SharedPropertyPane"/> 
        /// are ignored.
        /// </para>
        /// </remarks>
        protected override void OnDragEnter(System.Windows.Forms.DragEventArgs de)
        {
            base.OnDragEnter(de);
      
            IDataObject data = de.Data;
      
            if(!mToolboxService.IsToolboxItem(data))
            {
                de.Effect = DragDropEffects.None;
                return;
            }

            ToolboxItem ti = (ToolboxItem)mToolboxService.DeserializeToolboxItem(data);
            Type t = Type.GetType(ti.TypeName);
            if(t == null)
            {
                de.Effect = DragDropEffects.None;
                return;
            }

            if(typeof(PropertyPane).IsAssignableFrom(t) && !typeof(SharedPropertyPane).IsAssignableFrom(t))
                de.Effect = DragDropEffects.Copy;
            else
                de.Effect = DragDropEffects.None;
        }

        /// <summary>
        ///   Handle the drag-move event
        /// </summary>
        /// <param name="de">Object describing the drag-drop event that has occured</param>
        /// <remarks>
        /// <para>
        /// Update our internal drag-n-drop state as an OLE drag-n-drop object is being dragged
        /// around above this control.
        /// </para>
        /// </remarks>
        protected override void OnDragOver(System.Windows.Forms.DragEventArgs de)
        {
            base.OnDragOver(de);
            Point pt = mPropertyTree.TreeView.PointToClient(new Point(de.X,de.Y));
            PaneNode newSelPaneNode = mPropertyTree.GetPaneNodeAt(pt);
      
            if(newSelPaneNode != mTargetPaneNode)
            {
                if(newSelPaneNode != null)
                {
                    DropHilite(newSelPaneNode,true);
                }
                if(mTargetPaneNode != null)
                {
                    DropHilite(mTargetPaneNode,false);
                }
            }
      
            mTargetPaneNode = newSelPaneNode;
        }

        /// <summary>
        ///   Turns on or off drop-hiliting for the given PropertyPane's TreeNode.
        /// </summary>
        /// <param name="paneNode">
        /// The PaneNode whose TreeNode's hiliting is in question
        /// </param>
        /// <param name="hilite">
        /// Boolean indicating whether to turn drop-hiliting on or off
        /// </param>
        protected void DropHilite(PaneNode paneNode, bool hilite)
        {
            if(paneNode.TreeNode != null)
            {
                if(hilite)
                {
                    paneNode.TreeNode.BackColor = SystemColors.Highlight;
                    paneNode.TreeNode.ForeColor = SystemColors.HighlightText;
                }
                else
                {
                    paneNode.TreeNode.BackColor = mPropertyTree.TreeView.BackColor;
                    paneNode.TreeNode.ForeColor = mPropertyTree.TreeView.ForeColor;
                }
            }
        }

        /// <summary>
        ///   Generates a new unique name for a <see cref="PropertyPane"/> that is 
        ///   suitable for use as a variable name.
        /// </summary>
        /// <remarks>
        ///   <para>
        ///   This function returns names of the form "ppPaneN" where N is a number.  This
        ///   function will start at "ppPane1" and keep incrementing the trailing number
        ///   until it finds a name that has not been used yet.
        ///   </para>
        ///   <para>
        ///   The <see cref="IReferenceService.GetName"/> is used to determine whether
        ///   or not a name is in use by the designer.
        ///   </para>
        /// </remarks>
        /// <returns>
        /// A unique name for a <see cref="PropertyPane"/> that is suitable for 
        /// use as a variable name.
        /// </returns>
        protected string GenerateNewPaneName()
        {
            int curNum=1;
            bool bDuplicateName;
            IReferenceService refsvc = GetService(typeof(IReferenceService)) as IReferenceService;
            string curTry;

            // Get a new component name
            do
            {
                curTry = "ppPane" + curNum;
                bDuplicateName = (refsvc.GetReference(curTry) != null);
                curNum++;
            }
            while(bDuplicateName);

            return curTry;
        }

        /// <summary>
        /// Gets all of the child <see cref="PaneNode"/>s for the given 
        /// <paramref name="paneNode"/>.
        /// </summary>
        /// <param name="paneNode">
        /// The <c>PaneNode</c> for which to get children.
        /// </param>
        /// <returns>
        /// Returns an array containing all <see cref="PaneNode"/>s that
        /// have <paramref name="paneNode"/> as an ancestor.
        /// </returns>
        private PaneNode[] GetAllChildPaneNodes(PaneNode paneNode)
        {
            ArrayList temp = new ArrayList();
            GetAllChildPaneNodesHlpr(paneNode,temp);

            PaneNode[] children = new PaneNode[temp.Count];
            temp.CopyTo(children);

            return children;
        }

        /// <summary>
        /// Recursive helper function for 
        /// <see cref="PropertyTreeDesigner.GetAllChildPaneNodes"/>.
        /// </summary>
        /// <param name="parent">
        /// The <c>PaneNode</c> whose children are being inspected this recursion
        /// </param>
        /// <param name="childNodes">
        /// The list of all child <see cref="PaneNode"/>s found so far.
        /// </param>
        private void GetAllChildPaneNodesHlpr(PaneNode parent, ArrayList childNodes)
        {
            foreach(PaneNode child in parent.PaneNodes)
            {
                childNodes.Add(child);
                GetAllChildPaneNodesHlpr(child,childNodes);
            }
        }
    
        /// <summary>
        /// Updates the menu commands in preparation for displaying the
        /// context menu in the designer.
        /// </summary>
        /// <param name="x">The X coordinate to display the context menu</param>
        /// <param name="y">The Y coordinate to display the context menu</param>
        /// <remarks>
        /// This override actually does nothing more than update our menu
        /// commands' states, because we manually take care of determining
        /// when and where to should our context menu.
        /// </remarks>
        /// <seealso cref="PropertyTreeDesigner.WndProc"/>
        protected override void OnContextMenu(int x, int y)
        {
            UpdateMenuCommands();
        }
    }
}
